/* FreeRTOS.org includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* Demo includes. */
#include "basic_io.h"

/* Includes do oled */
#include "lpc17xx_pinsel.h"
#include "lpc17xx_gpio.h"
#include "lpc17xx_i2c.h"
#include "lpc17xx_ssp.h"
#include "lpc17xx_adc.h"
#include "lpc17xx_timer.h"
#include "oled.h"
#include "acc.h"

#define mainQUEUE_SEND_FREQUENCY_MS (1000/portTICK_RATE_MS)

/*------------------FUNַױES OLED---------------------*/
static uint8_t buf[10];
static uint8_t buf2[10];

typedef struct ptagAcel{

	int8_t x;
	int8_t y;
	int8_t z;

}ttag_Acel;

static void intToString(int value, uint8_t* pBuf, uint32_t len, uint32_t base){

    static const char* pAscii = "0123456789abcdefghijklmnopqrstuvwxyz";
    int pos = 0;
    int tmpValue = value;

    //  O buffer nדo pode ser nulo e pelo menos ter tamanho 2 para conter
    //  um dםgito e um terminador nulo.

    if (pBuf == NULL || len < 2){

        return;
    }

//    Uma base vבlida nדo pode ser menor que 2 ou maior que 36
//    o valor da base de 2 significa representaחדo binבria. Ovalor 1 significa apenas zeros
//    A base maior que 36 pode apenas ser usada se um alfabeto maior for usado.

    if (base < 2 || base > 36){

        return;
    }

    // valor negativo

    if (value < 0){

        tmpValue = -tmpValue;
        value    = -value;
        pBuf[pos++] = '-';
    }

    // calcula o valor necessבrio do buffer.

    do {

        pos++;
        tmpValue /= base;

    } while(tmpValue > 0);

    if (pos > len){

        // o parגmetro len י vבlido.
        return;
    }

    pBuf[pos] = '\0';

    do {

        pBuf[--pos] = pAscii[value % base];
        value /= base;

    } while(value > 0);

    return;

}

static void init_ssp(void){

    SSP_CFG_Type SSP_ConfigStruct;
    PINSEL_CFG_Type PinCfg;

    /*
     * Initialize SPI pin connect
     * P0.7 - SCK;
     * P0.8 - MISO
     * P0.9 - MOSI
     * P2.2 - SSEL - used as GPIO
     */
    PinCfg.Funcnum = 2;
    PinCfg.OpenDrain = 0;
    PinCfg.Pinmode = 0;
    PinCfg.Portnum = 0;
    PinCfg.Pinnum = 7;
    PINSEL_ConfigPin(&PinCfg);
    PinCfg.Pinnum = 8;
    PINSEL_ConfigPin(&PinCfg);
    PinCfg.Pinnum = 9;
    PINSEL_ConfigPin(&PinCfg);
    PinCfg.Funcnum = 0;
    PinCfg.Portnum = 2;
    PinCfg.Pinnum = 2;
    PINSEL_ConfigPin(&PinCfg);

    SSP_ConfigStructInit(&SSP_ConfigStruct);

    // Inicializa o perifיrico SSP com o parגmetro dado na estrutura acima
    SSP_Init(LPC_SSP1, &SSP_ConfigStruct);

    // Habilita o perifיrico SSP
    SSP_Cmd(LPC_SSP1, ENABLE);
}

static void init_i2c(void){

    PINSEL_CFG_Type PinCfg;

    /* Inicializa a conecחדo do pino I2C2 */
    PinCfg.Funcnum = 2;
    PinCfg.Pinnum = 10;
    PinCfg.Portnum = 0;
    PINSEL_ConfigPin(&PinCfg);
    PinCfg.Pinnum = 11;
    PINSEL_ConfigPin(&PinCfg);

    // Inicializa o perifיrico I2C
    I2C_Init(LPC_I2C2, 100000);

    /* Habilita a operaחדo I2C1 */
    I2C_Cmd(LPC_I2C2, ENABLE);
}

ttag_Acel vLeituraAcel ()
{
	ttag_Acel pDados;

	/* read signal of accelerometer */
	acc_read(&pDados.x,&pDados.y,&pDados.z);
	return pDados;
}

void vImpressao (ttag_Acel pDados)
{
	/* Conversão e escrita de x, y e z */
	intToString(pDados.x, buf, 10, 10);
	oled_fillRect(13,12, 80, 32, OLED_COLOR_WHITE);
	oled_putString(13,12,buf, OLED_COLOR_BLACK, OLED_COLOR_WHITE);

	intToString(pDados.y, buf, 10, 10);
	oled_fillRect(13,23, 80, 40, OLED_COLOR_WHITE);
	oled_putString(13,23, buf, OLED_COLOR_BLACK, OLED_COLOR_WHITE);

	intToString(pDados.z, buf, 10, 10);
	oled_fillRect(13,34, 80, 48, OLED_COLOR_WHITE);
	oled_putString(13,34, buf, OLED_COLOR_BLACK, OLED_COLOR_WHITE);
}

/*----------------FUNÇOES DA TASK------------------*/

/* As tarefas para serem criadas. Duas tasks sדo criadas
    para a tarefa "Enviar" enquanto apenas uma instגncia
    י criada para a tarefa "Receber". */
static void vSenderTask( void *pvParameters );

static void vReceiverTask( void *pvParameters );

static void vPeriodicTask( void *pvParameters );

/*-----------------------------------------------------------*/

/* Declara a variבvel do tipo xQueueHandle. Esta י usada para armazenar o queue
    que י acessado por todas as 3 tarefas. */

xQueueHandle xQueue;

int main (void) {

    init_i2c();
    init_ssp();
    acc_init();
    oled_init();

    oled_clearScreen(OLED_COLOR_WHITE);
    oled_putString(1,1,  (uint8_t*)"Acelerometro: ", OLED_COLOR_BLACK, OLED_COLOR_WHITE);
    oled_putString(1,12,(uint8_t*)"X:", OLED_COLOR_BLACK, OLED_COLOR_WHITE);
    oled_putString(1,23,(uint8_t*)"Y:", OLED_COLOR_BLACK, OLED_COLOR_WHITE);
    oled_putString(1,34,(uint8_t*)"Z:", OLED_COLOR_BLACK, OLED_COLOR_WHITE);
    /* O queue י criado para conter no mבximo 5 valores do tipo "long". */
    xQueue = xQueueCreate( 5, sizeof( struct ptagAcel * ) );

    if( xQueue != NULL )
    {

    /* Cria uma instגncia das tarefa que sera escrita no queue.
     * A tarefa י criada com prioridade 1. */
    	xTaskCreate( vSenderTask, "SenderLeitura", 240, NULL, 1, NULL );

        /* Cria a tarefa que vai ler do queue. A tarefa י criada com
          prioridade 2, entדo acima da prioridade das SenderTask. */
        xTaskCreate( vReceiverTask, "ReceiverImpressão", 240, NULL, 2, NULL );


        /* Inicia o scheduler, entדo as tarefas criadas sדo executadas. */
        vTaskStartScheduler();

    }
    else
    {
        printf("Problema ao criar a queue das tarefas");
    }
    return 0;
}

/*-----------------------------------------------------------*/

static void vSenderTask(void *pvParameters ){

	ttag_Acel pValueReceived;
    portBASE_TYPE xStatus1;

    /* Duas instגncias sדo criadas dessa tarefa entדo o valor que י enviado para o
       queue passou pelo parגmetro tarefa ao invיs de ser codificado.. Desta forma
       cada instגncia pode usar um valor diferente. Projetando o parגmetro para o
       tipo requerido. */

    /* Como em muitas tarefas, esta tarefa י implementada com um loop infinito. */
    for( ;; )
    {

        /*Atribui a variavel de envio, o valor retornado da funחao de leitura e conversדo da entrada bnc*/
    	pValueReceived = vLeituraAcel();

        /* O primeiro parגmetro informa a queue em que o dado serב depositado
         *
         * O segundo parגmetro י o dado a ser enviado;
         *
         * O terceiro parגmetro י o tempo de bloqueio, tempo que a tarefa deve ser mantida
            no estado bloqueado para esperar por espaחo para se tornar disponםvel no queue
            devendo o queue estar completamente cheio. Neste caso nףs nדo especificamos o
            tempo de bloqueio porque deverב sempre haver espaחo para o queue. */

    	xStatus1 = xQueueSendToBack( xQueue, (void*)&pValueReceived , 0 );

    	if( xStatus1 != pdPASS){
            /* Nףs nדo devemos escrever para o queue porque esteve cheio, isto deve
            ser um erro uma vez que o queue nunca deve conter mais de um םtem. */

            vPrintString( "Could not send to the queue.\r\n" );
        }


    }
}

/*-----------------------------------------------------------*/

static void vReceiverTask( void *pvParameters ){

	/* Declara a variבvel que irב conter os valores recebidos do queue. */

	ttag_Acel lReceived;
	portBASE_TYPE xStatus1;

	const portTickType xTicksToWait = 100 / portTICK_RATE_MS;

    /* Esta tarefa י tambיm definida com um loop infinito. */
    for( ;; )
    {

    	/* Assim que esta tarefa desbloquear imediatamente esta data י escrita para o queue, esta
           chamada deve sempre encontrar o queue vazio. */
    	if( uxQueueMessagesWaiting( xQueue ) != 0 )
    	{
            vPrintString( "A fila deveria estar vazia!\r\n" );
        }

        /*O primeiro parגmetro י o queue de onde os dados sדo para ser recebidos. O
           queue י criado antes do scheduler comeחar, e portanto antes da tarefa rodar
           pela primeira vez.
        *
        * O segundo parגmetro י o interior do buffer onde os dados recebidos serדo colocados.
        *
        * Neste caso o buffer י simplesmente o endereחo da variבvel que tem o tamanho necessבrio
        * para conter os dados recebidos.
        *
        * O תltimo parגmetro י o bloqueador de tempo que י o tempo mבximo que a
        * tarefa deve permanecer no estado bloqueado para esperar pelos dados
        * estarem disponםveis devendo o queue estar sempre.*/
    	xStatus1 = xQueueReceive( xQueue, &(lReceived), xTicksToWait );

    	if( xStatus1 == pdPASS){
            /* Dados foram recebidos com sucesso do queue, imprime os valores recebidos no oled */

    		vImpressao(lReceived);
        }

        else{

            vPrintString( "Nada foi recebido do queue mesmo depois de esperar 100ms.\n "
            "Deve ter havido um erro, uma vez que a tarefa enviar esta rodando livremente e sera continuamente escrita no queue.\r\n" );
        }
    }
}

/*-----------------------------------------------------------*/

void vApplicationMallocFailedHook( void ){

    /* Esta funחדo vai rodar apenas se na chamada de uma API (para criar uma tarefa, queue
        ou semגforo) haver uma falha porque existe pouca memפria RAM faltando. */

    for( ;; );
}

/*-----------------------------------------------------------*/

void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName ){

    /* Esta funחדo vai ser chamada apenas se a tarefa trasborda na pilha,
        nota que a verificaחדo de estouro de pilha faz abrandar a mudanחa
        de contexto implementaחדo. */

    for( ;; );
}

/*-----------------------------------------------------------*/

void vApplicationIdleHook( void ){

    /* Este exemplo nדo usa o gancho "idle" para realizar qualquer processamento. */
}

/*-----------------------------------------------------------*/

void vApplicationTickHook( void ){

    /* Este exemplo nדo usa o gancho "tick" para realizar qualquer processamento. */
}
